﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Transactions;
using System.Reflection;

namespace _1Dot9MasterTransactions
{
    internal static class TransactionController
    {
        // Fields
        private static Hashtable _TransactionInfoTable = new Hashtable(0x40);

        // Methods
        public static TransactionInfo EnterTransactionContext(MethodBase method, out TransactionScope scope)
        {
            TransactionInfo txInfo = ResolveTransactionInfo(method);
            TransactionScope _scope = null;
            if (txInfo != null)
            {
                TransactionOptions options = new TransactionOptions();
                options.IsolationLevel = txInfo.TxIsolationLevel;
                options.Timeout = new TimeSpan(0, 600, txInfo.TimeOut);
                _scope = new TransactionScope(txInfo.TxTransactionOption, options, EnterpriseServicesInteropOption.Automatic);
            }
            scope = _scope;
            return txInfo;
        }

        private static string GetApplicationName(Type type)
        {
            Assembly assembly = type.Assembly;
            object[] attrs = assembly.GetCustomAttributes(typeof(ApplicationNameAttribute), true);
            if (attrs.Length > 0)
            {
                return ((ApplicationNameAttribute)attrs[0]).ApplicationName;
            }
            return assembly.GetName().Name;
        }

        public static void LeaveTransactionContext(TransactionInfo txInfo, Exception ex, TransactionScope scope)
        {
            if (!txInfo.IsManualComplete)
            {
                if (ex == null)
                {
                    SetComplete(txInfo, scope);
                }
                else
                {
                    SetAbort();
                }
            }
            TransactionScope _Scope = scope;
            if (_Scope != null)
            {
                _Scope.Dispose();
            }
        }

        private static TransactionInfo ResolveTransactionInfo(MethodBase method)
        {
            TransactionInfo txInfo = (TransactionInfo)_TransactionInfoTable[method];
            bool isManualComplete = false;
            if (txInfo == null)
            {
                object[] attributes = method.GetCustomAttributes(true);
                if (attributes.Length > 0)
                {
                    Type type = method.ReflectedType;
                    foreach (Attribute attr in attributes)
                    {
                        if (attr is TransactionAttribute)
                        {
                            txInfo = new TransactionInfo((TransactionAttribute)attr);
                            if (txInfo.TransactionOption == TransactionOption.None)
                            {
                                return null;
                            }
                            txInfo.ClassName = type.FullName;
                            txInfo.ApplicationName = GetApplicationName(type);
                        }
                        else if (attr is ManualCompleteAttribute)
                        {
                            isManualComplete = ((ManualCompleteAttribute)attr).IsManualComplete;
                        }
                    }
                    if (txInfo != null)
                    {
                        txInfo.IsManualComplete = isManualComplete;
                        _TransactionInfoTable.Add(method, txInfo);
                    }
                }
            }
            if (txInfo == null)
            {
                txInfo = (TransactionInfo)_TransactionInfoTable[method.ReflectedType];
                if (txInfo != null)
                {
                    return txInfo;
                }
                object[] attributes = method.ReflectedType.GetCustomAttributes(true);
                if (attributes.Length <= 0)
                {
                    return txInfo;
                }
                Type type = method.ReflectedType;
                foreach (Attribute attr in attributes)
                {
                    if (attr is TransactionAttribute)
                    {
                        txInfo = new TransactionInfo((TransactionAttribute)attr);
                        if (txInfo.TransactionOption == TransactionOption.None)
                        {
                            return null;
                        }
                        txInfo.ClassName = type.FullName;
                        txInfo.ApplicationName = GetApplicationName(type);
                    }
                    else if (attr is ManualCompleteAttribute)
                    {
                        isManualComplete = ((ManualCompleteAttribute)attr).IsManualComplete;
                    }
                }
                if (txInfo != null)
                {
                    txInfo.IsManualComplete = isManualComplete;
                    _TransactionInfoTable.Add(method.ReflectedType, txInfo);
                }
            }
            return txInfo;
        }

        internal static void SetAbort()
        {
        }

        internal static void SetComplete(TransactionInfo txInfo, TransactionScope scope)
        {
            TransactionScope _scope = scope;
            if (_scope != null)
            {
                _scope.Complete();
            }
        }
    }
}
